package com.tj.api.rest.account;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fqgj.common.utils.DateUtil;
import com.fqgj.common.utils.MD5;
import com.fqgj.exception.common.ApplicationException;
import com.jsoniter.output.JsonStream;
import com.tj.api.annotations.SignIgnore;
import com.tj.api.annotations.VisitorAccessible;
import com.tj.api.rest.account.requset.DepositNotifyRequest;
import com.tj.api.rest.account.response.DepositPageResponse;
import com.tj.api.rest.account.response.DepositRecordResponse;
import com.tj.api.rest.account.response.DepositStatusResponse;
import com.tj.bo.account.client.AccountQueryService;
import com.tj.bo.account.client.DepositService;
import com.tj.bo.account.client.enums.DepositStatusEnum;
import com.tj.bo.account.client.vo.*;
import com.tj.bo.common.conf.api.ApiResponse;
import com.tj.bo.common.conf.config.RedisConfigUtil;
import com.tj.bo.common.conf.util.SerialNumberUtil;
import com.tj.bo.payment.client.PayCommandService;
import com.tj.bo.payment.client.PayQueryService;
import com.tj.bo.payment.client.enums.PayResultEnum;
import com.tj.bo.payment.client.vo.BusinessVO;
import com.tj.bo.payment.client.vo.PayNotificationResolveVO;
import com.tj.bo.payment.client.vo.PayNotificationVO;
import com.tj.bo.payment.client.vo.PaymentChannelVO;
import com.tj.bo.user.client.UserQueryService;
import com.tj.bo.user.client.vo.output.UserQueryOutput;
import com.tj.template.config.vo.RequestLocalInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * User: anonymous
 * Date: 2020/06/11
 * Time: 下午2:16
 */
@RestController
@RequestMapping("/deposit")
public class DepositController {
    private static final Logger logger = LoggerFactory.getLogger(DepositController.class);
    private static final String SECRET = "YKVISMFTMWXSUECAYZGKAYV";
    @Autowired
    private AccountQueryService accountQueryService;
    @Autowired
    private RedisConfigUtil accountConfig;
    @Autowired
    private DepositService depositService;
    @Autowired
    private RedisConfigUtil redisDbConfigUtil;
    @Autowired
    private PayCommandService payCommandService;
    @Autowired
    private PayQueryService payQueryService;
    @Autowired
    private UserQueryService userQueryService;

    /**
     * 获取充值通道列表
     *
     * @return
     */
    @RequestMapping("/page")
    public ApiResponse<DepositPageResponse> depositPage() {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();

        String depositAmountProductList = accountConfig.getDepositAmountProductList();
        List<DepositAmountProductVO> depositAmountProductVOList = JSONArray.parseArray(depositAmountProductList, DepositAmountProductVO.class);

        String activityImgUrl = redisDbConfigUtil.getActivityImgUrl();
        if (depositService.hasDepositSuccessRecord(userCode)) {
            activityImgUrl = null;
        }
        // TODO: 2021/7/2 暂时没有支付通道 
//        List<DepositChannelVO> depositChannelList = depositService.getDepositChannelList(userCode, true);
        PaymentChannelVO paymentChannelVO = payQueryService.getPaymentChannels().get(0);
        DepositChannelVO depositChannelVO = new DepositChannelVO();
        depositChannelVO.setServiceFeeRate(paymentChannelVO.getServerFeeRate());
        depositChannelVO.setPayChannel(paymentChannelVO.getCode());
        depositChannelVO.setPayChannelName(paymentChannelVO.getName());
        depositChannelVO.setTip(paymentChannelVO.getName());
        return new ApiResponse<>(new DepositPageResponse(depositAmountProductVOList, Collections.singletonList(depositChannelVO), activityImgUrl));
    }


    /**
     * 充值
     *
     * @param
     * @return
     */
    @SignIgnore
    @VisitorAccessible
    @RequestMapping(value = "", produces = "text/html")
    public String deposit(@RequestParam String depositAmountCode, @RequestParam String amount, @RequestParam String channelCode, @RequestParam String realToken) {
        DepositAmountProductVO depositAmountProductVO = getDepositAmountProductVO(depositAmountCode);
        UserQueryOutput userQueryOutput = userQueryService.getUserInfoByRealToken(realToken);
        AccountVO accountInfo = accountQueryService.getAccountInfo(userQueryOutput.getUserBaseReal().getUserCode());

//        DepositChannelVO depositChannel = depositService.getDepositChannel(Integer.parseInt(channelCode), true);
        PaymentChannelVO paymentChannelVO = payQueryService.getPaymentChannels().get(0);

        DepositVO depositVO = new DepositVO();
        depositVO.setUserCode(userQueryOutput.getUserBaseReal().getUserCode());
        depositVO.setAccountNo(accountInfo.getAccountNo());
        depositVO.setDepositAmountCode(depositAmountProductVO.getCode());
        depositVO.setAmount(depositAmountProductVO.getAmount());
        depositVO.setGrants(depositAmountProductVO.getGrants());
        depositVO.setDepositChannel(paymentChannelVO.getName());
        depositVO.setDepositChannelCode(paymentChannelVO.getCode());
        //depositVO.setAppVersion(RequestLocalInfo.getRequestBasicInfo().getAppVersion());
        Object deposit = depositService.deposit(depositVO);
        return (String) deposit;
    }

    /**
     * 充值结果异步回调
     */
    //@RequestMapping("/notify")
    @VisitorAccessible
    @SignIgnore
    public String depositNotify(@RequestBody DepositNotifyRequest depositNotifyRequest) {
        if (depositNotifyRequest == null) {
            logger.error("== notify null ==");
        } else {
            logger.info("充值结果异步回调:depositNotifyRequest:{}", JSON.toJSONString(depositNotifyRequest));
        }

        return "OK";
    }

    @VisitorAccessible
    @SignIgnore
    @RequestMapping(value = "/notify", produces = MediaType.TEXT_PLAIN_VALUE)
    @Transactional(rollbackFor = Exception.class)
    public String payNotify(@RequestParam(required = false) String memberid, @RequestParam(required = false) String orderid, @RequestParam(required = false) String amount,
                            @RequestParam(required = false) String datetime, @RequestParam(required = false) String returncode, @RequestParam(required = false) String transaction_id,
                            @RequestParam(required = false) String attach, @RequestParam(required = false) String sign) {
        //{"memberid":["210796892"],"orderid":["20210720150
        //736d51685PM000007"],"transaction_id":["20210720150706504910"],"amount":["10.0000"],"datetime":["20210720150901"],"returncode":["00"],"sign":["97ACA6EEC390E799E467B99B09CA7E9E"],"attach":["20210720150736d5
        //1685DP000006"]}
        String log = "memberid=" + memberid + "," +
                "orderid=" + orderid + "," +
                "amount=" + amount + "," +
                "datetime=" + datetime + "," +
                "returncode=" + returncode + "," +
                "transaction_id=" + transaction_id + "," +
                "attach=" + attach + "," +
                "sign=" + sign;
        logger.info("=======支付回调开始======, " + log);
        try {
            PayNotificationVO payNotificationVO = new PayNotificationVO();
            payNotificationVO.setMerchantId(memberid);
            payNotificationVO.setOrderId(orderid);
            payNotificationVO.setAmount(amount);
            payNotificationVO.setDatetime(datetime);
            payNotificationVO.setReturnCode(returncode);
            payNotificationVO.setTransactionId(transaction_id);
            payNotificationVO.setAttach(attach);
            payNotificationVO.setSign(sign);
            PayNotificationResolveVO resolveVO = payCommandService.resolvePayNotification(payNotificationVO);
            DepositNotifyVO depositNotifyVO = new DepositNotifyVO();
            if (Objects.equals(PayResultEnum.SUCCESS, resolveVO.getPayResult())) {
                BusinessVO businessVO = resolveVO.getBusiness();
                depositNotifyVO.setCode("0000");
                depositNotifyVO.setDepositOrderNo(attach);
                depositNotifyVO.setAcceptAmount(new BigDecimal(businessVO.getAmount()));
                depositNotifyVO.setAcceptNo(businessVO.getPayOrderCode());
                //depositNotifyVO.setAppVersion(depositNotifyRequest.getAppVersion());
                //depositNotifyVO.setInfoOrder(depositNotifyRequest.getInfoOrder());
            } else {
                depositNotifyVO.setCode("00");
                depositNotifyVO.setDepositOrderNo(attach);
                depositNotifyVO.setAcceptAmount(new BigDecimal(amount));
            }
            depositService.depositNotify(depositNotifyVO);
            return resolveVO.getDes();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("支付回调异常", e);
            return "FATAL";
        }
    }

    @VisitorAccessible
    @SignIgnore
    @RequestMapping(value = "/notify2", produces = MediaType.TEXT_PLAIN_VALUE)
    public String payNotify2(@RequestParam(required = false) String memberid, @RequestParam(required = false) String orderid, @RequestParam(required = false) String amount,
                             @RequestParam(required = false) String datetime, @RequestParam(required = false) String returncode, @RequestParam(required = false) String transaction_id,
                             @RequestParam(required = false) String attach, @RequestParam(required = false) String sign) {
        //{"memberid":["210796892"],"orderid":["20210720150
        //736d51685PM000007"],"transaction_id":["20210720150706504910"],"amount":["10.0000"],"datetime":["20210720150901"],"returncode":["00"],"sign":["97ACA6EEC390E799E467B99B09CA7E9E"],"attach":["20210720150736d5
        //1685DP000006"]}
        String log = "66666666 memberid=" + memberid + "," +
                "orderid=" + orderid + "," +
                "amount=" + amount + "," +
                "datetime=" + datetime + "," +
                "returncode=" + returncode + "," +
                "transaction_id=" + transaction_id + "," +
                "attach=" + attach + "," +
                "sign=" + sign;
        logger.info("=======支付回调开始======, " + log);
        return "OK";

    }

    public static void main(String[] args) {
        String memberid="210796892";
        String orderid="20210720154037d51816PM000002";

        String amount="10.0000";

        String datetime="20210720154200";

        String returncode="00";

        String transaction_id = "20210720154007102505";

        String keyValue="0bo4eg8a28otnlndo5qjvasceo17gjfj";

        String SignTemp="amount="+amount+"+datetime="+datetime+"+memberid="+memberid+"+orderid="+orderid+"+returncode="+returncode+"+transaction_id="+transaction_id+"+key="+keyValue+"";


        String s = MD5.md5(SignTemp);

        Map<String, String> params = new HashMap<>();
        String payOrderCode = SerialNumberUtil.getBizOrderNumber("P", "M");
        params.put("memberid", "210796892");
        params.put("orderid", "20210720154037d51816PM000002");
        params.put("amount", "10.0000");
        params.put("datetime", "20210720154200");
        params.put("returncode", "00");
        params.put("transaction_id", "20210720154007102505");
//        params.put("key", "0bo4eg8a28otnlndo5qjvasceo17gjfj");

        logger.info("== 发起支付==,request:" + JsonStream.serialize(params));
        List<String> keys = params.keySet().stream()
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        String stringSignTemp = keys.stream()
                .map(key -> {
                    Object value = params.get(key);
                    return key + "=" + value.toString();
                }).collect(Collectors.joining("&"))
                .concat("&key=" + "0bo4eg8a28otnlndo5qjvasceo17gjfj");


        String stringSignTemp1 = MD5.md5(stringSignTemp);


        System.err.println(11);
    }


    /**
     * 获取最新一笔充值订单
     *
     * @return
     */
    @RequestMapping("/query/latest/order")
    public ApiResponse<DepositStatusResponse> queryLatestOrder() {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        DepositVO depositVO = depositService.queryLatestDepositOrder(userCode);
        DepositStatusResponse depositStatusResponse = getDepositStatusResponseByStatus(depositVO.getDepositStatusEnum());
        depositStatusResponse.setChannel(depositVO.getDepositChannel());
        depositStatusResponse.setAmount(depositVO.getAmount().stripTrailingZeros().toPlainString());
        if (depositVO.getGrants() != null && BigDecimal.ZERO.compareTo(depositVO.getGrants()) < 0) {
            depositStatusResponse.setNeedPopup(true);
            depositStatusResponse.setPopupContent("Congratulations! Your bonus has arrived!");
            depositStatusResponse.setGrants(depositVO.getGrants().stripTrailingZeros().toPlainString());
        }
        depositStatusResponse.setFirstDeposit(depositService.getDepositRecordListByUserCode(userCode) == 1 ? true : false);
        return new ApiResponse<>(depositStatusResponse);
    }

    /**
     * 获取充值记录
     *
     * @return
     */
    @RequestMapping("/query/order/list")
    public ApiResponse<DepositRecordResponse> queryOrderList() {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        List<DepositVO> depositRecordList = depositService.getDepositRecordList(userCode);
        List<DepositRecordResponse.DepositRecordVO> records = new ArrayList<>();
        for (DepositVO depositVO : depositRecordList) {
            DepositRecordResponse.DepositRecordVO depositRecordVO = new DepositRecordResponse.DepositRecordVO();
            depositRecordVO.setAmount("PKR" + depositVO.getAmount());
            depositRecordVO.setDate(DateUtil.getDate(depositVO.getGmtCreate(), "yyyy-MM-dd HH:mm:ss"));
            depositRecordVO.setStatus(depositVO.getDepositStatusEnum().getCode());
            depositRecordVO.setStatusText(depositVO.getDepositStatusEnum().getDesc());
            records.add(depositRecordVO);
        }
        return new ApiResponse<>(new DepositRecordResponse(records));
    }

    private DepositAmountProductVO getDepositAmountProductVO(String depositAmountCode) {
        String depositAmountProductListStr = accountConfig.getDepositAmountProductList();
        List<DepositAmountProductVO> depositAmountProductList = JSONArray.parseArray(depositAmountProductListStr, DepositAmountProductVO.class);
        for (DepositAmountProductVO depositAmountProductVO : depositAmountProductList) {
            if (depositAmountProductVO.getCode().equals(depositAmountCode)) {
                return depositAmountProductVO;
            }
        }
        throw new ApplicationException("Please select amount");
    }

    private DepositStatusResponse getDepositStatusResponseByStatus(DepositStatusEnum depositStatusEnum) {
        String depositStatusConfig = accountConfig.getDepositStatusConfig();
        List<DepositStatusResponse> depositStatusConfigList = JSONArray.parseArray(depositStatusConfig, DepositStatusResponse.class);
        for (DepositStatusResponse depositStatusResponse : depositStatusConfigList) {
            if (depositStatusResponse.getStatus() == depositStatusEnum.getCode()) {
                return depositStatusResponse;
            }
        }
        return null;
    }

    private boolean verifyNotifySignature(String timestamp, String signature) {
        String calculatedSignature = MD5.md5(timestamp + SECRET);
        logger.info("timestamp:{},signature:{},calculatedSignature:{}", timestamp, signature, calculatedSignature);
        return Objects.equals(signature, calculatedSignature);
    }
}